package com.yvan.zipkin;

import com.yvan.DateUtils;
import com.yvan.YvanUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;

/**
 * Created by Administrator on 2018/1/15.
 */
public class TracingCollector {

    final Logger LOGGERZIPKIN = LoggerFactory.getLogger("ZIPKIN");

    private TracingCollectorType tracingCollectorType;

    private String serviceName;

    private String fullName;

    private String traceId;

    private String id;

    private String parentId;

    private Integer port;

    private ArrayList<Span> spans;

    private TracingCollector() {
        spans = new ArrayList();
    }

    public static TracingCollector create(String applicationName, Integer port, String traceId, String id, String parentId, TracingCollectorType type) {
        TracingCollector collector = new TracingCollector();
        collector.setServiceName(applicationName);
        collector.setPort(port);
        collector.setTraceId(traceId != null ? traceId : YvanUtil.createUUID());
        collector.setId(id);
        collector.setParentId(parentId);
        collector.setTracingCollectorType(type);
        return collector;
    }

    public String getTraceId() {
        return traceId;
    }

    public void setTraceId(String traceId) {
        this.traceId = traceId;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getParentId() {
        return parentId;
    }

    public void setParentId(String parentId) {
        this.parentId = parentId;
    }

    public TracingCollectorType getTracingCollectorType() {
        return tracingCollectorType;
    }

    public void setTracingCollectorType(TracingCollectorType tracingCollectorType) {
        this.tracingCollectorType = tracingCollectorType;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    private Span createSpan() {
        Span span = new Span();
        span.setId(getId());
        span.setName(getServiceName());
        span.setTraceId(getTraceId());
        span.setParentId(getParentId());
        return span;
    }

    private Annotation createAnnotation() {
        Annotation annotation = new Annotation();
        Endpoint endpoint = new Endpoint();
        String hostIp = YvanUtil.getHostIp();
        endpoint.setIpv4(hostIp);
        endpoint.setPort(getPort());
        endpoint.setServiceName(getServiceName());
        annotation.setEndpoint(endpoint);
        annotation.setTimestamp(DateUtils.now().getTime() * 1000);
        annotation.setValue(tracingCollectorType.getName());
        return annotation;
    }

    private BinaryAnnotation createAnnotation(String key, String value) {
        BinaryAnnotation annotation = new BinaryAnnotation();
        Endpoint endpoint = new Endpoint();
        String hostIp = YvanUtil.getHostIp();
        endpoint.setIpv4(hostIp);
        endpoint.setPort(getPort());
        endpoint.setServiceName(getServiceName());
        annotation.setEndpoint(endpoint);
        annotation.setKey(key);
        annotation.setValue(value);
        return annotation;
    }


    private BinaryAnnotation createExceptionAnnotation(Exception e) {
        return createAnnotation("exception", e.getMessage());
    }

    public void beforeInvoke() {
        StackTraceElement stack[] = Thread.currentThread().getStackTrace();
        String className = stack[2].getClassName();
        String methodName = stack[2].getMethodName();
        setFullName(className + "-" + methodName);
        Span rootSpan = createSpan();
        rootSpan.addAnnotation(createAnnotation());
        spans.add(0, rootSpan);
    }

    public void addException(Exception e) {
        Span rootSpan = spans.get(0);
        Annotation beforeAnnotation = rootSpan.getAnnotations().get(0);
        BinaryAnnotation annotation = createExceptionAnnotation(e);
        if (beforeAnnotation != null && beforeAnnotation.getTimestamp() != null) {
            annotation.setDuration(DateUtils.now().getTime() * 1000 - beforeAnnotation.getTimestamp());
        }
        rootSpan.addAnnotation(annotation);
    }

    public void afterInvoke() {
        afterInvoke(null);
    }

    public void afterInvoke(String traceId) {
        Span rootSpan = spans.get(0);
        if (traceId != null) {
            rootSpan.setTraceId(traceId);
        }
        Annotation annotation = rootSpan.getAnnotations().get(0);
        if (annotation != null && annotation.getTimestamp() != null) {
            annotation.setDuration(DateUtils.now().getTime() * 1000 - annotation.getTimestamp());
        }
        LOGGERZIPKIN.info(YvanUtil.toJson(spans));
    }
}
